#!/usr/bin/env node
/**
 * 🗄️ PPH数据库自动备份系统
 * 
 * 功能：
 * 1. 定时自动备份数据库
 * 2. 备份文件管理和清理
 * 3. 备份完整性验证
 * 4. 备份恢复功能
 */

const fs = require('fs');
const path = require('path');
const cron = require('node-cron');

class DatabaseBackup {
  constructor() {
    // 计算仓库根目录（自下而上查找包含 config 和 app 的目录）
    const findRepoRoot = () => {
      try {
        let dir = __dirname;
        for (let i = 0; i < 6; i++) {
          const hasConfig = fs.existsSync(path.join(dir, 'config'));
          const hasApp = fs.existsSync(path.join(dir, 'app'));
          if (hasConfig && hasApp) return dir;
          dir = path.resolve(dir, '..');
        }
        // 回退：从当前脚本位置向上4级，通常即仓库根
        return path.resolve(__dirname, '../../../..');
      } catch { return path.resolve(__dirname, '../../../..'); }
    };
    const repoRoot = findRepoRoot();

    // 将相对路径统一转换为以“仓库根目录”为基准的绝对路径
    const resolveFromRepo = (raw, fallback) => {
      const base = (raw && String(raw).trim().length > 0) ? raw : fallback;
      try { return path.isAbsolute(base) ? base : path.resolve(repoRoot, base); } catch { return fallback; }
    };

    const rawDbPath = process.env.DB_PATH || 'app/backend/data/pph.sqlite';
    this.dbPath = resolveFromRepo(rawDbPath, path.join(repoRoot, 'app', 'backend', 'data', 'pph.sqlite'));

    // 兼容两种环境变量名：DB_BACKUP_DIR 优先，其次 BACKUP_DIR
    const rawBackupDir = process.env.DB_BACKUP_DIR || process.env.BACKUP_DIR || (process.platform === 'win32' ? 'D:\\PPH-Backups' : '/app/backups');
    this.backupDir = resolveFromRepo(rawBackupDir, (process.platform === 'win32' ? 'D:\\PPH-Backups' : '/app/backups'));

    this.maxBackups = parseInt(process.env.MAX_BACKUPS) || 35; // 保留35个备份文件（7天 × 5个/天）
    this.isRunning = false;

    // 确保备份目录存在
    this.ensureBackupDirectory();
  }

  /**
   * 确保备份目录存在
   */
  ensureBackupDirectory() {
    if (!fs.existsSync(this.backupDir)) {
      fs.mkdirSync(this.backupDir, { recursive: true });
      console.log('📁 创建备份目录:', this.backupDir);
    }
  }

  /**
   * 生成备份文件名
   */
  generateBackupFileName() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const hour = String(now.getHours()).padStart(2, '0');
    const minute = String(now.getMinutes()).padStart(2, '0');
    const second = String(now.getSeconds()).padStart(2, '0');
    
    return `pph-backup-${year}${month}${day}-${hour}${minute}${second}.sqlite`;
  }

  /**
   * 执行数据库备份
   */
  async createBackup() {
    if (this.isRunning) {
      console.log('⚠️ 备份任务正在运行中，跳过本次备份');
      return false;
    }

    this.isRunning = true;
    
    try {
      // 检查源数据库文件是否存在
      if (!fs.existsSync(this.dbPath)) {
        throw new Error(`数据库文件不存在: ${this.dbPath}`);
      }

      const backupFileName = this.generateBackupFileName();
      const backupFilePath = path.join(this.backupDir, backupFileName);

      console.log('📦 开始备份数据库...');
      console.log(`   源文件: ${this.dbPath}`);
      console.log(`   备份文件: ${backupFilePath}`);

      // 复制数据库文件
      fs.copyFileSync(this.dbPath, backupFilePath);

      // 验证备份文件
      const sourceStats = fs.statSync(this.dbPath);
      const backupStats = fs.statSync(backupFilePath);

      if (sourceStats.size !== backupStats.size) {
        throw new Error('备份文件大小不匹配');
      }

      // 创建备份信息文件
      const backupInfo = {
        timestamp: new Date().toISOString(),
        sourceFile: this.dbPath,
        backupFile: backupFilePath,
        sourceSize: sourceStats.size,
        backupSize: backupStats.size,
        checksum: this.calculateChecksum(backupFilePath)
      };

      const infoFilePath = backupFilePath.replace('.sqlite', '.json');
      fs.writeFileSync(infoFilePath, JSON.stringify(backupInfo, null, 2));

      console.log(`✅ 数据库备份成功: ${backupFileName}`);
      console.log(`   文件大小: ${(backupStats.size / 1024 / 1024).toFixed(2)} MB`);

      // 清理旧备份
      await this.cleanupOldBackups();

      return true;

    } catch (error) {
      console.error('❌ 数据库备份失败:', error.message);
      return false;
    } finally {
      this.isRunning = false;
    }
  }

  /**
   * 计算文件校验和（简单版本）
   */
  calculateChecksum(filePath) {
    const crypto = require('crypto');
    const fileBuffer = fs.readFileSync(filePath);
    const hashSum = crypto.createHash('md5');
    hashSum.update(fileBuffer);
    return hashSum.digest('hex');
  }

  /**
   * 清理旧备份文件
   */
  async cleanupOldBackups() {
    try {
      const files = fs.readdirSync(this.backupDir)
        .filter(file => file.startsWith('pph-backup-') && file.endsWith('.sqlite'))
        .map(file => ({
          name: file,
          path: path.join(this.backupDir, file),
          stats: fs.statSync(path.join(this.backupDir, file))
        }))
        .sort((a, b) => b.stats.mtime - a.stats.mtime); // 按修改时间降序排列

      if (files.length > this.maxBackups) {
        const filesToDelete = files.slice(this.maxBackups);
        
        console.log(`🧹 清理旧备份文件，保留最新 ${this.maxBackups} 个备份`);
        
        for (const file of filesToDelete) {
          fs.unlinkSync(file.path);
          
          // 同时删除对应的信息文件
          const infoFile = file.path.replace('.sqlite', '.json');
          if (fs.existsSync(infoFile)) {
            fs.unlinkSync(infoFile);
          }
          
          console.log(`   删除: ${file.name}`);
        }
        
        console.log(`✅ 已清理 ${filesToDelete.length} 个旧备份文件`);
      }
    } catch (error) {
      console.error('❌ 清理旧备份失败:', error.message);
    }
  }

  /**
   * 列出所有备份文件
   */
  listBackups() {
    try {
      const files = fs.readdirSync(this.backupDir)
        .filter(file => file.startsWith('pph-backup-') && file.endsWith('.sqlite'))
        .map(file => {
          const filePath = path.join(this.backupDir, file);
          const infoPath = filePath.replace('.sqlite', '.json');
          const stats = fs.statSync(filePath);
          
          let info = null;
          if (fs.existsSync(infoPath)) {
            try {
              info = JSON.parse(fs.readFileSync(infoPath, 'utf8'));
            } catch (e) {
              // 忽略信息文件读取错误
            }
          }
          
          return {
            name: file,
            path: filePath,
            size: stats.size,
            created: stats.birthtime,
            modified: stats.mtime,
            info: info
          };
        })
        .sort((a, b) => b.modified - a.modified);

      console.log('📋 数据库备份列表:');
      files.forEach((file, index) => {
        const sizeStr = (file.size / 1024 / 1024).toFixed(2) + ' MB';
        const dateStr = file.modified.toLocaleString('zh-CN');
        console.log(`${index + 1}. ${file.name} (${sizeStr}, ${dateStr})`);
      });

      return files;
    } catch (error) {
      console.error('❌ 列出备份文件失败:', error.message);
      return [];
    }
  }

  /**
   * 恢复数据库备份
   */
  async restoreBackup(backupFileName) {
    try {
      const backupFilePath = path.join(this.backupDir, backupFileName);
      
      if (!fs.existsSync(backupFilePath)) {
        throw new Error(`备份文件不存在: ${backupFileName}`);
      }

      // 创建当前数据库的紧急备份
      const emergencyBackup = this.generateBackupFileName().replace('pph-backup-', 'emergency-backup-');
      const emergencyBackupPath = path.join(this.backupDir, emergencyBackup);
      
      if (fs.existsSync(this.dbPath)) {
        fs.copyFileSync(this.dbPath, emergencyBackupPath);
        console.log(`📦 已创建紧急备份: ${emergencyBackup}`);
      }

      // 恢复备份
      fs.copyFileSync(backupFilePath, this.dbPath);
      
      console.log(`✅ 数据库恢复成功: ${backupFileName}`);
      console.log(`   紧急备份: ${emergencyBackup}`);
      
      return true;
    } catch (error) {
      console.error('❌ 数据库恢复失败:', error.message);
      return false;
    }
  }

  /**
   * 启动定时备份任务
   */
  startScheduledBackup() {
    console.log('🕐 启动数据库定时备份任务...');

    // 每天凌晨2点备份
    cron.schedule('0 2 * * *', async () => {
      console.log('🕐 [02:00] 开始执行定时数据库备份...');
      await this.createBackup();
    }, {
      timezone: "Asia/Shanghai"
    });

    // 每6小时备份一次（可选，用于重要数据）
    cron.schedule('0 */6 * * *', async () => {
      console.log('🕐 [每6小时] 开始执行增量数据库备份...');
      await this.createBackup();
    }, {
      timezone: "Asia/Shanghai"
    });

    console.log('✅ 数据库定时备份已启动');
    console.log('   - 每天凌晨2点执行完整备份');
    console.log('   - 每6小时执行增量备份');
  }

  /**
   * 停止定时备份任务
   */
  stopScheduledBackup() {
    // PM2会自动管理cron任务的生命周期
    console.log('⏹️ 定时备份任务已停止');
  }

  /**
   * 获取备份状态
   */
  getStatus() {
    const backups = this.listBackups();
    const latestBackup = backups.length > 0 ? backups[0] : null;
    
    return {
      isRunning: this.isRunning,
      backupDir: this.backupDir,
      maxBackups: this.maxBackups,
      totalBackups: backups.length,
      latestBackup: latestBackup ? {
        name: latestBackup.name,
        size: latestBackup.size,
        created: latestBackup.created
      } : null
    };
  }
}

// 导出类
module.exports = DatabaseBackup;

// 如果直接运行此脚本
if (require.main === module) {
  const backup = new DatabaseBackup();
  
  const command = process.argv[2];
  
  switch (command) {
    case 'create':
      backup.createBackup();
      break;
    case 'list':
      backup.listBackups();
      break;
    case 'restore':
      const backupFile = process.argv[3];
      if (!backupFile) {
        console.error('请指定要恢复的备份文件名');
        process.exit(1);
      }
      backup.restoreBackup(backupFile);
      break;
    case 'start':
      backup.startScheduledBackup();
      break;
    case 'status':
      console.log(JSON.stringify(backup.getStatus(), null, 2));
      break;
    default:
      console.log('用法:');
      console.log('  node database-backup.cjs create   - 创建备份');
      console.log('  node database-backup.cjs list     - 列出备份');
      console.log('  node database-backup.cjs restore <文件名> - 恢复备份');
      console.log('  node database-backup.cjs start    - 启动定时备份');
      console.log('  node database-backup.cjs status   - 查看状态');
  }
}
